/*
This piece of code pings all other nodes and logs the clock data for all nodes.
Its helpful for comparison.
*/
#include "../common/config.h"
#define PORT_LOG 5057
#define D_T 5000000 //5s

struct sockaddr_in addr_w[USERS], my_addr;		//Stores addresses of all nodes
char add_w[USERS][16], my_add[16];			//Stores addresses of all nodes in ascii format
int sockfd_clklog;		//Socket for logging data
clock_t start_time, current_time, node_times[USERS];	//Stores and updates clock values as defined by clock()
long double tm_clock, node_tm[USERS], avg_rtt[USERS];	//Stores and updates clock values as defined by the user defined function time_virt
char ch[4]="4";			//Node number of node running the data log (this function) in char form
unsigned long recvd[USERS];

typedef struct				//Message for logging data
{
  int type, machine;			//type defines the message type. Ensure that this is the first element.
//  clock_t curr_time;
  long double strt_time;
  long double virt_time;
  unsigned long iteratn;
} msg_clklog;

void *appsendlog();
void *apprecvlog();
void *time_virtual();

int main()
{
  int i, ret3, ret4, ret5;
  pthread_t thsendlog, threcvlog, thtime;
  if((sockfd_clklog = socket(AF_INET, SOCK_DGRAM, 0))==-1)	//Messages exchanged over the wired network using simple sockets
    {
      perror("socket");
      exit(1);
    }
  strcpy(my_add, ipclass_w);
  strcat(my_add, ch);		//Binding the class and node IP address of the node logging data

  for(i=0;i<USERS;i++)	        //Binding the class and the node IPs
    {
      strcpy(add_w[i], ipclass_w);
      strcat(add_w[i], node[i]);
    }
  for(i=0;i<USERS;i++)		//Network format being used is struct sockaddr_in
    {
      addr_w[i].sin_family = AF_INET;		//Always AF_INET for us
      addr_w[i].sin_port = htons(PORT_LOG);	//Port
      addr_w[i].sin_addr.s_addr = inet_addr(add_w[i]);	//Changing the format of IP address from ascii (XXX.XXX.XXX.XXX format) to that understandable by the sockets and network
      memset(&(addr_w[i].sin_zero), '\0', 8);	//Needs to be done for type casting sockaddr_in to sockaddr type
    }

  hno = server_check(ch);		//Check the index corresponding to this node
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(PORT_LOG);
  my_addr.sin_addr.s_addr = inet_addr(my_add);
  memset(&(my_addr.sin_zero), '\0', 8);

  //printf("Binding %s\n", my_add);
  if((ret5=pthread_create( &thtime, NULL, &time_virtual, NULL)))	//Time_virtual keeps the user defined clock running
   {
      	printf("Thread creation failed: %d\n", ret5);
   }
  if(bind(sockfd_clklog, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1)
    {
      perror("bind");
      exit(1);
    }
//  sleep(2);
  if((ret3=pthread_create( &thsendlog, NULL, &appsendlog, NULL)))	//Thread for  sending log messages
   {
      	printf("Thread creation failed: %d\n", ret3);
   }
  if((ret4=pthread_create( &threcvlog, NULL, &apprecvlog, NULL)))	//Thread for receiving log messages
   {
        printf("Thread creation failed: %d\n", ret4);
   }
  pthread_join(thsendlog, NULL);
//  fclose(f);
  return 0;
}
void *time_virtual()	//Function storing virtual times
{
  tm_clock = 0.0;
  start_time = clock();
  while(1)
  {
    usleep(2);
    tm_clock+=.000020;
    current_time = clock();
  }
}

void *appsendlog()		//Function sending log messages
{
  int i;
  unsigned long j = 0;
  msg_clklog *logmsg;
  while(1)
    {
      usleep(D_T);		//Sending different sets of log messages at different times
      if(j==0)			//Initializations being done here for first message.
	{
	  start_time = clock();
	  for(i=0;i<USERS;i++)
	    {
		node_times[i] = start_time;
		avg_rtt[i] = 0.0;	//Average round trip time, time when message was finally received - time when it was sent.
		recvd[i]=0;
	    }
	}
      logmsg = (msg_clklog *)malloc(sizeof(msg_clklog));
      for(i=0;i<USERS;i++)
	{
	  logmsg->type = MT_LOGMSG;
//	  logmsg->curr_time = clock();
	  logmsg->machine=i;
	  logmsg->iteratn=j;
	  logmsg->strt_time = tm_clock;			//Start time for the message log sent in message
	  logmsg->virt_time = -1.0000;		//This just helps in checking if the node running the logging actually stored data
	  //printf("Sent %d, %lu\n", i, j);
	  if(sendto(sockfd_clklog, logmsg, sizeof(msg_clklog), 0, (struct sockaddr *) &addr_w[i], sizeof(struct sockaddr))==-1)
	    {
	      perror("sendto");
	      continue;
	    }
	}
      j++;
      free(logmsg);
    }
  return (NULL);
}
void *apprecvlog()
{
  int numbytes, i;
  unsigned long j;
  unsigned add_len;
  msg_clklog *logmsg;
  char *msg_ch;
  msg_ch = (char *)malloc(sizeof(msg_clklog) + sizeof(char));	//Message is received in type char but this can gotten to be rid of as only one type of message is being received
  struct sockaddr_in their_addr;
  FILE *f;
  if((f=fopen("f_clk", "w"))==NULL)		//File for writing the logged data
    {
      printf("Can't open file to write\n");
      exit(1);
    }

    fprintf(f, "node number, null, null, local clock (1000 sec), remote clock (1000 sec)\n");


 while(1)
    {
      add_len = sizeof(struct sockaddr);
      if((numbytes=recvfrom(sockfd_clklog, msg_ch, sizeof(msg_clklog)+sizeof(char), 0, (struct sockaddr *) &their_addr, &add_len)) == -1)
	{
	  perror("recvfrom");
	  continue;
	}
      else if(numbytes>0)	//If a message was received
	{
	  logmsg = (msg_clklog *)msg_ch;
	  i = logmsg->machine;
	  j = logmsg->iteratn;
//	  node_times[i]=logmsg->curr_time;
	  avg_rtt[i] = (avg_rtt[i]*recvd[i] + (tm_clock - logmsg->strt_time)/2)/(recvd[i] + 1);	//Calculating the average time taken for a round trip of a log message sent and received
	  node_tm[i] = logmsg->virt_time + avg_rtt[i];
//	  printf("%d, %llf\n", i, logmsg ->virt_time);
//	  printf("Received: %d, %lu, %llf\n", i, j, ((logmsg->curr_time - start_time + 0.0L)/CLOCKS_PER_SEC) );
          current_time = clock();
	  fprintf(f, "%d, %llf, %llf, %llf, %llf\n", i, ((current_time - start_time + 0.0L)/CLOCKS_PER_SEC) , ((node_times[i] - start_time + 0.0L)/CLOCKS_PER_SEC), tm_clock, node_tm[i] );	//Logging the data received alongwith current time
	  fflush(f);
	}
    }
  free(logmsg);
  fclose(f);
  return (NULL);
}
